using System;
using System.Collections.Generic;
using System.Text;
using CommonSupport;
using System.Runtime.Serialization;
using System.Drawing;


namespace CommonFinancial
{
    /// <summary>
    /// Base class for all technical trading indicators implementations (like MA, MACD, EMA, RSI etc.).
    /// Note that one mathematics indicator algorythm (for ex. RSI) can have multiple implementations in 
    /// multiple classes (for ex. RSI1, RSI2, RSIEx etc.).
    /// IndicatorsUnsafe are separated in two main types 
    /// - tradeable indicators produce results and signals in a consistent way, by using only information from the past periods
    /// - non tradeable indicators use information that may not be available in a normal typical trading environment (like for ex. bars from the future)
    /// If you are using an indicator for a trading strategy testing, make sure it is actually Tradeable.
    /// </summary>
    [Serializable]
    public abstract class Indicator : IDeserializationCallback, IDisposable
    {
        protected IndicatorResults _results;
        /// <summary>
        /// Results generated by this indicator.
        /// </summary>
        public IndicatorResults Results
        {
            get
            {
                if (this.Enabled == false)
                {
                    return null;
                }

                // Results has thread safety integrated.
                return _results;
            }
        }

        protected IndicatorSignals _signals;
        /// <summary>
        /// Signals generated by this indicator (signals are usually processed from results
        /// and present an interpreted version of the resuls, suitable for futher automated processing)
        /// </summary>
        public IndicatorSignals Signals
        {
            get
            {
                if (this.Enabled == false)
                {
                    return null;
                }
                // Signals has thread safety integrated.
                return _signals;
            }
        }

        protected IndicatorParameters _parameters;
        /// <summary>
        /// Provides dynamic access to any type of indicator parameters.
        /// Needed, since some indicators are accessible only trough management classes, 
        /// and effectively share the same class, with their separate instances.
        /// </summary>
        public IndicatorParameters Parameters
        {
            get
            {
                // Parameters has thread safety integrated.
                return _parameters;
            }
        }

        [NonSerialized]
        private IDataBarHistoryProvider _dataProvider;
        
        /// <summary>
        /// The data provider used by the indicator.
        /// </summary>
        public IDataBarHistoryProvider DataProvider
        {
            get
            {
                lock (this)
                {
                    return _dataProvider;
                }
            }
        }

        /// <summary>
        /// Is the indicator initialized.
        /// </summary>
        public bool Initialized
        {
            get { lock (this) { return _dataProvider != null; } }
        }

        volatile protected string _name = string.Empty;
        /// <summary>
        /// The name of the indicator.
        /// </summary>
        public string Name
        {
            get { return _name; }
        }

        volatile protected string _description = string.Empty;
        /// <summary>
        /// A description of this indicator.
        /// </summary>
        public string Description
        {
            get { return _description; }
        }

        volatile bool _enabled = true;
        /// <summary>
        /// Is the indicator enabled.
        /// </summary>
        public bool Enabled
        {
            get { return _enabled; }
        }

        bool? _tradeable;
        /// <summary>
        /// Null value here means - do not know.
        /// </summary>
        public bool? Tradeable
        {
            get { return _tradeable; }
        }

        bool? _scaledToQuotes;
        /// <summary>
        /// Null value here means = do not know.
        /// </summary>
        public bool? ScaledToQuotes
        {
            get { return _scaledToQuotes; }
        }

        Dictionary<string, Color> _customMessages = null;
        /// <summary>
        /// A set of additional output message of this indicator to the user.
        /// </summary>
        public Dictionary<string, Color> CustomMessages
        {
            get { return _customMessages; }
            set { _customMessages = value; }
        }

        bool _isUnstable = false;
        /// <summary>
        /// Unstable indicatos calculate their current value based on *all* the previously 
        /// calculated values; thus their effective lookback is changing and growing all the time.
        /// </summary>
        public bool IsUnstable
        {
            get { return _isUnstable; }
        }

        public delegate void IndicatorCalculatedDelegate(Indicator indicator, bool fullRecalculation);
        
        /// <summary>
        /// Event fires when indicator has been calculated.
        /// </summary>
        [field:NonSerialized]
        public event IndicatorCalculatedDelegate IndicatorCalculatedEvent;

        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="name">Pass null or empty to use the UserFriendly name attribute (applicable for custom indicators, not to GenericTaLib ones).</param>
        /// <param name="isTradeable"></param>
        /// <param name="isScaledToQuotes">Is the indicator the same number scale as quote dataDelivery (for ex. a same scaled indicator is a MA indicator, and a RSI is not)</param>
        public Indicator(string name, bool? isTradeable, bool? isScaledToQuotes, string[] resultSetNames)
        {
            _name = name;
            if (string.IsNullOrEmpty(_name))
            {
                _name = GetUserFriendlyName();
            }
            _tradeable = isTradeable;
            _scaledToQuotes = isScaledToQuotes;

            _results = new IndicatorResults(this, resultSetNames);
            _signals = new IndicatorSignals(this);
            _parameters = new IndicatorParameters();
        }

        public virtual void Dispose()
        {
            UnInitialize();
            _dataProvider = null;
        }

        #region IDeserializationCallback Members

        public virtual void OnDeserialization(object sender)
        {
        }

        #endregion

        public virtual bool Initialize(IDataBarHistoryProvider dataProvider)
        {
            SystemMonitor.CheckThrow(_dataProvider == null, "Data provider already assigned.");
            TracerHelper.Trace(this.Name);
            lock (this)
            {
                _dataProvider = dataProvider;
                if (_dataProvider != null)
                {
                    _dataProvider.DataBarHistoryUpdateEvent += new DataBarHistoryUpdateDelegate(_dataProvider_DataBarHistoryUpdateEvent);
                }

                if (_parameters != null)
                {
                    _parameters.ParameterUpdatedValueEvent += new IndicatorParameters.ParameterUpdatedValueDelegate(_parameters_ParameterUpdatedValueEvent);
                }
            }


            return true;
        }

        public virtual void UnInitialize()
        {
            lock (this)
            {
                if (_dataProvider != null)
                {
                    if (_dataProvider != null)
                    {
                        _dataProvider.DataBarHistoryUpdateEvent -= new DataBarHistoryUpdateDelegate(_dataProvider_DataBarHistoryUpdateEvent);
                    }
                    _dataProvider = null;
                }

                if (_parameters != null)
                {
                    _parameters.ParameterUpdatedValueEvent -= new IndicatorParameters.ParameterUpdatedValueDelegate(_parameters_ParameterUpdatedValueEvent);
                }
            }
        }

        void _parameters_ParameterUpdatedValueEvent(string name, object value)
        {
            Calculate(true, null);
        }

        void _dataProvider_DataBarHistoryUpdateEvent(IDataBarHistoryProvider provider, DataBarUpdateType updateType, int updatedBarsCount)
        {
            Calculate(false, updateType);
        }

        /// <summary>
        /// Child classes must be marked with UserFriendlyAttribute, to provide user friendly name.
        /// </summary>
        /// <returns></returns>
        public string GetUserFriendlyName()
        {
            string name = this.GetType().Name;
            UserFriendlyNameAttribute.GetTypeAttributeValue(this.GetType(), ref name);
            return name;
        }

        /// <summary>
        /// Calculation helper function.
        /// </summary>
        public void Calculate(bool fullRecalculation, DataBarUpdateType? updateType)
        {
            if (this.Initialized == false || this.Enabled == false)
            {
                return;
            }

            if (_dataProvider.BarCount == 0)
            {
                return;
            }

            // Calculate the indicator level lines.
            OnCalculate(fullRecalculation, updateType);

            //// Start the mostly child performed signal analisys.
            //Signals.PerformCrossingResultAnalysis(ProvideSignalAnalysisLines());

            //Signals.PerformExtremumResultAnalysis();

            if (IndicatorCalculatedEvent != null)
            {
                IndicatorCalculatedEvent(this, fullRecalculation);
            }
        }

        /// <summary>
        /// Perform calculation on the given piece of dataDelivery.
        /// </summary>
        protected abstract void OnCalculate(bool fullRecalculation, DataBarUpdateType? updateType);

        ///// <summary>
        ///// Line indeces always come from lower to biger.
        ///// </summary>
        ///// <param name="currentSignalPositionValue">This is what currently the value for this position is. Allows to handle multiple signals at a given place.</param>
        //virtual public float OnResultAnalysisCrossingFound(int line1index, double line1value, int line2index, double line2value, bool direction, double currentPositionSignalValue)
        //{
        //    return 0;
        //}

        ///// <summary>
        ///// Line indeces always come from lower to biger.
        ///// </summary>
        ///// <param name="currentSignalPositionValue">This is what currently the value for this position is. Allows to handle multiple signals at a given place.</param>
        //virtual public float OnResultAnalysisExtremumFound(int lineIndex, double lineValue, bool extremumDirection, double currentPositionSignalValue)
        //{
        //    return 0;
        //}

        ///// <summary>
        ///// The child should return null in case it want no calculation to be done, or not inherit at all.
        ///// </summary>
        ///// <returns></returns>
        //virtual protected double[][] ProvideSignalAnalysisLines()
        //{
        //    return null;
        //}

    }
}
